package com.example.gnenboot.demos.service.impl;


import com.example.gnenboot.demos.domain.AnalysisNMFReq;
import com.example.gnenboot.demos.domain.AnalysisReq;
import com.example.gnenboot.demos.service.IRadapterService;
import com.example.gnenboot.demos.utils.*;
import lombok.extern.slf4j.Slf4j;

import org.rosuda.JRI.REXP;
import org.rosuda.JRI.Rengine;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.*;

import java.util.stream.Collectors;

@Slf4j
@Service
public class RadapterServiceImpl implements IRadapterService {
    @Value("${dataFilePath}")
    private String dataFilePath;
    @Value("${resultFilePath}")
    private String resultFilePath;
    @Value("${sumResultFilePath}")
    private String sumResultFilePath;
    @Value("${zipResultFilePath}")
    private String zipResultFilePath;
    @Value("${resultFileCount}")
    private String countFilePath;
    @Value("${geneFile}")
    private String geneFile;

   /* public RadapterServiceImpl(Rengine rengine, Rengine engine) {
        this.engine = engine;
        this.rengine = rengine;
    }*/

    public List<String> list(){
        return getFiles(dataFilePath);
    }
    public List<Map<String,Object>> targetFiles(){
        return getFileObjs(dataFilePath);
    }
    public void upload(MultipartFile[] files) throws IOException {
        for (MultipartFile file : files) {
            if (file.isEmpty()) {
                continue;
            }
            FileUploadUtils.upload(dataFilePath,file);
        }
    }
    public void analysis(AnalysisReq param){
        Rengine engine = SingleRengine.getInstance();
        try {
            String expFiles = param.getExpFiles();
            //String geneFile = param.getGene();
            engine.eval("library(Themis2)");
            engine.eval("library(ConsensusClusterPlus)");
            engine.assign("expFile", expFiles);
            engine.assign("geneFile", geneFile);
            engine.assign("directory", resultFilePath);
            engine.assign("path", sumResultFilePath);

            engine.assign("maxKParam", String.valueOf(param.getMaxK()));
            engine.assign("repsParam", String.valueOf(param.getReps()));
            engine.assign("pItemParam", String.valueOf(param.getPItem()));
            engine.assign("clusterNumParam", String.valueOf(param.getClusterNum()));
            engine.assign("clusterAlgParam", param.getClusterAlg());
            engine.assign("distanceParam", param.getDistance());
            engine.assign("pFeatureParam", String.valueOf(param.getPFeature()));
            engine.assign("seedParam", String.valueOf(param.getSeed()));
            engine.assign("plotParam", param.getPlot());

            engine.eval("maxK <- as.numeric(maxKParam)");
            engine.eval("reps <- as.numeric(repsParam)");
            engine.eval("pItem <- as.numeric(pItemParam)");
            engine.eval("clusterNum <- as.numeric(clusterNumParam)");
            engine.eval("clusterAlg <- clusterAlgParam");
            engine.eval("distance <- distanceParam");
            engine.eval("pFeature <- as.numeric(pFeatureParam)");
            engine.eval("seed <- as.numeric(seedParam)");
            engine.eval("plot <- plotParam");

            engine.eval("exp <- expFile");
            engine.eval("gene <- geneFile");
            engine.eval("dir <- directory");
            engine.eval("p <- path");

            engine.eval("setwd(dir)");

            /*REXP dir = engine.eval("print(dir)");
            REXP gene = engine.eval("print(gene)");
            REXP exp = engine.eval("print(exp)");
            REXP p = engine.eval("print(p)");

            REXP a = engine.eval("print(max)");
            REXP e = engine.eval("print(reps)");
            REXP m = engine.eval("print(pItem)");
            REXP n = engine.eval("print(clusterNum)");
            REXP c = engine.eval("print(clusterAlg)");
            REXP d = engine.eval("print(distance)");
            REXP f = engine.eval("print(pFeature)");
            REXP s = engine.eval("print(seed)");
            REXP o = engine.eval("print(plot)");


            log.info("eval :: print param dir="+dir);
            log.info("eval :: print param gene="+gene);
            log.info("eval :: print param exp="+exp);
            log.info("eval :: print param p="+p);

            log.info("eval :: print param max="+a);
            log.info("eval :: print param reps="+e);
            log.info("eval :: print param pItem="+m);
            log.info("eval :: print param clusterNum="+n);
            log.info("eval :: print param clusterAlg="+c);
            log.info("eval :: print param distance="+d);
            log.info("eval :: print param pFeature="+f);
            log.info("eval :: print param seed="+s);
            log.info("eval :: print param plot="+o);*/

            log.info("call function ConsensusClusterPlusAnalysis,start.....");
            REXP cr = engine.eval("ConsensusClusterPlusAnalysis(exp,gene,dir,maxK,reps,pItem,clusterNum,clusterAlg,distance,pFeature,seed,plot)");
            log.info("call function ConsensusClusterPlusAnalysis,end.....");
            log.info("call function ConsensusClusterPlusAnalysis,return value = "+cr);
            engine.eval("Themis_custom_method(dir, p)");
            engine.eval("Themis_process(dir)");
            engine.eval("Themis_output(dir)");

            REXP nmfReturn = engine.eval("print(val$num_files)");

            log.info("ConsensusClusterPlusAnalysis retrun ="+nmfReturn);
            log.info("R program end...");
            engine.end();

            if(nmfReturn!=null){
                Double rFileNums = nmfReturn.asDouble();
                saveCount(rFileNums.intValue());
            }
        } catch (Exception ex) {
            throw new RuntimeException(ex);
            //engine.end();
        }finally {
            // 可选的清理代码，无论是否发生异常都会执行
            engine.end();
        }
    }

   /* private Rengine engine;
    @Autowired
    private Rengine rengine;*/
   /* public RExecutor(Rengine engine) {
        this.rengine = engine;
    }*/
    /*public void analysisNMF2(AnalysisNMFReq param){

        //ExecutorService executor = Executors.newFixedThreadPool(20);
        Rengine engine = new Rengine(null, false, null);

        String expFile = param.getExpFiles();
        String geneFile = param.getGene();

        //Rengine engine = Rengine.getMainEngine();//空指针
        if (!engine.waitForR()) {
            log.info("Cannot load R");
            return;
        }
        engine.eval("library(Themis2)");
        engine.eval("library(NMF)");
        engine.assign("expFile", expFile);
        engine.assign("geneFile", geneFile);
        engine.assign("directory", resultFilePath);
        engine.assign("path", sumResultFilePath);

        engine.assign("methodParam", param.getMethod());
        engine.assign("nrunParam", String.valueOf(param.getNrun()));
        engine.assign("seedParam", String.valueOf(param.getSeed()));
        engine.assign("clusterNumParam", String.valueOf(param.getClusterNum()));

        engine.eval("exp <- expFile");
        engine.eval("gene <- geneFile");
        engine.eval("dir <- directory");
        engine.eval("p <- path");

        engine.eval("methodVal <- methodParam");
        engine.eval("nrunVal <- nrunParam");
        engine.eval("seedVal <- seedParam");
        engine.eval("clusterNumVal <- clusterNumParam");

        engine.eval("setwd(dir)");

        REXP dir = engine.eval("print(dir)");
        REXP gene = engine.eval("print(gene)");
        REXP exp = engine.eval("print(exp)");
        REXP p = engine.eval("print(p)");

        REXP m = engine.eval("print(methodVal)");
        REXP u = engine.eval("print(nrunVal)");
        REXP e = engine.eval("print(seedVal)");
        REXP c = engine.eval("print(clusterNumVal)");

        log.info("eval :: print param dir="+dir);
        log.info("eval :: print param gene="+gene);
        log.info("eval :: print param exp="+exp);
        log.info("eval :: print param p="+p);

        log.info("eval :: print param method="+m);
        log.info("eval :: print param nrun="+u);
        log.info("eval :: print param seed="+e);
        log.info("eval :: print param clusterNum="+c);

        log.info("call function NMFAnalysis,start.....");
        //REXP nr = engine.eval("val <- (exp,gene,dir,clusterNum = 2,method = \"brunet\",nrun = 10,seed = 123456)");//生成了一堆文件
        REXP nr = engine.eval("val <- NMFAnalysis(exp,gene,dir,clusterNum = 2,method = \"brunet\",nrun = 10,seed = 123456)");//生成了一堆文件

        log.info("call function NMFAnalysis,end.....");
        //log.info("call function NMFAnalysis,return value = "+nr);
        engine.eval("Themis_custom_method(dir, p)");
        engine.eval("Themis_process(dir)");
        engine.eval("Themis_output(dir)");

        REXP nmfReturn = engine.eval("print(val$num_files)");
        Double rFileNums = nmfReturn.asDouble();

        log.info("NMFAnalysis retrun ="+nmfReturn);
        log.info("NMFAnalysis retrun rFileNums ="+rFileNums);

        log.info("R program end...");
        engine.end();

        if(rFileNums!=null){
            saveCount(rFileNums.intValue());
        }

    }*/

    public void toZip() {
        File dir = new File(resultFilePath);
        //压缩后zip包文件路径
        String zipName = "zip_"+System.currentTimeMillis()+".zip";
        File dest = new File(zipResultFilePath+zipName);
        OutputStream outputStream = null;
        try {
            outputStream = new FileOutputStream(dest);
            ZipUtils.zip(dir, outputStream);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    public void saveCount(Integer num){
        Integer writeNum = num==null?0:num;
        File countFile = new File(countFilePath);
        if (countFile.exists()){
            // 文件存在，删除文件
            countFile.delete();
        }else{
            // 文件不存在，创建文件
            try {
                boolean created = countFile.createNewFile();
                if (created) {
                    FileWriter writer = new FileWriter(countFile);
                    writer.write(writeNum+"\n");
                    writer.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 工作目录必需先创建好，系统不会自动创建目录
     * 建议先写R脚本调试，没问题后再用JRI写一遍
     * 工作目录中要创建sum data目录，否则输出的文件会很少，因为Themis_custom_method等三个方法没有执行
     * path的路径必需在工作目录里面
     * 不要在函数参数中使用赋值语句，（如 NMFAnalysis(expFile,geneFile,directory,clusterNum = clusterNum,method = method,nrun = nrun,seed = seed)）会出错，不会收到返回值。
     * 应该写成下面形式 NMFAnalysis(expFile,geneFile,directory,clusterNum,method,nrun,seed)
     */
    public void analysisNMF(AnalysisNMFReq param){
        Rengine engine = SingleRengine.getInstance();
        try {
            /**
             * 获取工作目录，基因输入文件，分析肿瘤数据文件，汇总目录资源URL
             */
            String expFile = param.getExpFiles();
            //String geneFile = param.getGene();
            /**
             * 导入R依赖库
             */
            engine.eval("library(Themis2)");
            engine.eval("library(NMF)");
            /**
             * 获取NMFAnalysis 函数参数
             * R声明变量
             */
            engine.assign("expFile", expFile);
            engine.assign("geneFile", geneFile);
            engine.assign("directory", resultFilePath);
            engine.assign("path", sumResultFilePath);

            engine.assign("methodParam", param.getMethod());
            engine.assign("nrunParam", String.valueOf(param.getNrun()));
            engine.assign("seedParam", String.valueOf(param.getSeed()));
            engine.assign("clusterNumParam", String.valueOf(param.getClusterNum()));
            /**
             *
             * R变量赋值
             */
            engine.eval("exp <- expFile");
            engine.eval("gene <- geneFile");
            engine.eval("dir <- directory");
            engine.eval("p <- path");
            engine.eval("method <- methodParam");
            engine.eval("nrun <- nrunParam");
            engine.eval("seed <- seedParam");
            engine.eval("clusterNum <- clusterNumParam");
            /**
             *
             * R变量数据类型转换
             * 注：JRI上下文传递的参数都是string类型的，需要在R环境中转换为相应的数据类型，否则函数调用时会报错
             */

            engine.eval("cluster_fun_param <- as.numeric(clusterNumParam)");
            engine.eval("nrun_fun_param <- as.numeric(nrun)");
            engine.eval("seed_fun_param <- as.numeric(seed)");
            /**
             * 设置工作目录，输入分析结果文件的路径
             */
            engine.eval("setwd(dir)");
            /**
             * 执行函数 NMFAnalysis
             */
            engine.eval("val <- NMFAnalysis(exp,gene,dir,cluster_fun_param,method,nrun_fun_param,seed_fun_param)");
            /**
             * 执行图表绘制函数
             */
            engine.eval("Themis_custom_method(dir, p)");
            engine.eval("Themis_process(dir)");
            engine.eval("Themis_output(dir)");
            /**
             * 函数执行完毕后，取返回结果
             */
            REXP nmfReturn = engine.eval("print(val$num_files)");

            log.info("NMFAnalysis retrun ="+nmfReturn);

            log.info("R program end...");
            engine.end();

            if(nmfReturn!=null){
                /**
                 * 把 NMFAnalysis 函数返回的文件数量写入文件，为打包程序使用，打包程序判断文件是否全部生成，否则不打包
                 */
                Double rFileNums = nmfReturn.asDouble();
                saveCount(rFileNums.intValue());
            }
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }finally {
            engine.end();
        }
    }


    public void del(List<String> files,int type){
        if(type==0){
            delFiles(files,dataFilePath);
        }else {
            delFiles(files,resultFilePath);
        }
    }

    public void delAll(String path){
            File dir = new File(path);
            if (dir.exists()) {
                if (dir.isDirectory()) {
                    File[] files = dir.listFiles();
                    for (File f : files) {
                        if (f.isFile()) {
                            f.delete();
                        } else if (f.isDirectory()) {
                            delAll(f.getAbsolutePath());
                        }
                    }
                    dir.delete();
                } else {
                    dir.delete();
                }
            }
    }


    private void delFiles(List<String> files,String path){
        for(String n:files){
            File file = new File(path+"/"+n);
            file.delete();
        }
    }

    public List<String> show(){
        return getFiles(resultFilePath);
    }
    public void delete(List<String> filePaths){
        for(String path:filePaths){
            File dir = new File(path);
            dir.delete();
        }
    }
    private List<String> getFiles(String name){
        List<String> resule = new ArrayList<>();
        File dir = new File(name);
        File[] files = dir.listFiles();
        if(files!=null){
            resule = Arrays.stream(files).map(f->f.getName()).collect(Collectors.toList());
        }
        return resule;
    }
    private List<Map<String,Object>> getFileObjs(String name){
        List<Map<String,Object>> resule = new ArrayList<>();
        File dir = new File(dataFilePath);
        File[] files = dir.listFiles();
        if(files!=null){
            resule = Arrays.stream(files).map(f->{
                Map<String,Object> row = new HashMap<>();
                row.put("name",f.getName());
                //row.put("absolutePath",f.getAbsolutePath());
                row.put("path",f.getPath());
                return row;
            }).collect(Collectors.toList());
        }
        return resule;
    }
    public File getLastModifiedFile(){
        File dir = new File(zipResultFilePath);
        File[] files = dir.listFiles();
        List<File> result = new ArrayList<>();

        if(files!=null){
            result = Arrays.stream(files).filter( f->{
                String fname = f.getName();
                String extName = StringUtils.substring(fname,fname.lastIndexOf("."));
                        return extName.equals(".zip")?true:false;
                    }).sorted(Comparator.comparing(file->file.getName()))
                    .collect(Collectors.toList());
        }
        if(result.size()>0){
            return result.get(result.size()-1);
        }
        return null;

    }

    public Boolean isFileAllBuilding(){
        Integer count = 0;
        File finishFile = new File(countFilePath);
        File dir = new File(resultFilePath);
        File[] files = dir.listFiles();
        try {
            BufferedReader reader = new BufferedReader(new FileReader(finishFile));
            String line;
            while ((line = reader.readLine()) != null) {
                count = Integer.valueOf(line);
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        return files.length>=count?true:false;
    }
    public void writeParamFile(String content){
        try{
            File gene = new File(geneFile);
            if(gene==null){
                gene.createNewFile();
            }
            FileWriter writer = new FileWriter(gene);
            writer.write(content);
            writer.close();
        }catch (Exception ex){
            ex.printStackTrace();
        }

    }
    public String call(String param){
        Rengine engine = SingleRengine.getInstance();
        String filePath = dataFilePath+"cell.R";
        engine.assign("fileName", filePath);
        engine.eval("source(fileName)");
        REXP rexp = engine.eval("atom ()");
        Double d = rexp.asDouble();
        return d!=null?String.valueOf(d):"0";
    }

    public String sysCommand(String param){
        String result = "";
        try {
            String filePath = dataFilePath+"cell.R";
            // 执行系统命令，例如："ls" 或 "dir"
            Process process = Runtime.getRuntime().exec("D:\\project\\gene\\doc\\app.bat 12 45");

            // 读取命令执行结果
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                result+=line;
            }

            // 等待命令执行完成
            process.waitFor();

            // 获取退出值
            result+=";code:"+process.exitValue();
        }catch (Exception ex){
            ex.printStackTrace();
        }
        return result;
    }
    public String sysInit(String param){
        String result = "";
        try {
            String filePath = dataFilePath+"cell.R";
            // 执行系统命令，例如："ls" 或 "dir"
            Process process = Runtime.getRuntime().exec("conda activate R4.3.3");

            // 读取命令执行结果
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                result+=line;
            }

            // 等待命令执行完成
            process.waitFor();

            // 获取退出值
            result+="init:"+process.exitValue();
        }catch (Exception ex){
            ex.printStackTrace();
        }
        return result;
    }

    public static void main( String[] args ){
        //System.out.println( "call R themis!" );
        String geneFile = "D:\\Rworks\\demo\\gene.txt";
        String expFile = "D:\\Rworks\\demo\\data\\ACC_norm.txt";
        String directory = "D:\\Rworks\\demo\\result";
        String path = "D:\\Rworks\\demo\\result\\sum data";
        themis(expFile,geneFile,directory,path);
        //themis();
        //callFunction();
        /*String hc = "ACC\nBCC\nDCC\nECC\n";
        System.out.println(hc);*/
    }

    public static void themis(String expFile,String geneFile,String directory,String path){
        Rengine engine = new Rengine(null, false, null);
        if (!engine.waitForR()) {
            System.out.println("Cannot load R");
            return;
        }
        engine.eval("library(Themis2)");
        engine.eval("library(ConsensusClusterPlus)");
        engine.eval("library(NMF)");
        engine.assign("expFile", expFile);
        engine.assign("geneFile", geneFile);
        engine.assign("directory", directory);
        engine.assign("path", path);
        engine.eval("exp <- expFile");
        engine.eval("gene <- geneFile");
        engine.eval("dir <- directory");
        engine.eval("p <- path");
        engine.eval("setwd(dir)");
        REXP dir = engine.eval("print(dir)");
        REXP gene = engine.eval("print(gene)");
        REXP exp = engine.eval("print(exp)");
        REXP p = engine.eval("print(p)");
        System.out.println("eval :: print param dir="+dir);
        System.out.println("eval :: print param gene="+gene);
        System.out.println("eval :: print param exp="+exp);
        System.out.println("eval :: print param p="+p);
        //System.out.println("call function ConsensusClusterPlusAnalysis,start.....");
        //REXP cr = engine.eval("ConsensusClusterPlusAnalysis(exp, gene, dir,maxK = 9, reps = 1000, pItem = 0.8,clusterNum = 2,clusterAlg = \"km\", distance = \"pearson\", pFeature = 1,seed = 123456, plot = \"png\")");
        //System.out.println("call function ConsensusClusterPlusAnalysis,end.....");
        //System.out.println("call function ConsensusClusterPlusAnalysis,return value = "+cr);
        System.out.println("call function NMFAnalysis,start.....");
        REXP nr = engine.eval("NMFAnalysis(exp,gene,dir,clusterNum = 2,method = \"brunet\",nrun = 10,seed = 123456)");
        System.out.println("call function NMFAnalysis,end.....");
        System.out.println("call function NMFAnalysis,return value = "+nr);
        engine.eval("Themis_custom_method(dir, p)");
        engine.eval("Themis_process(dir)");
        engine.eval("Themis_output(dir)");
        System.out.println("R program end...");
        engine.end();
    }


}
